#pragma once

#include <iostream>
#include <functional>
#include <unordered_map>
#include <cassert>
#include "Log.hpp"
#include "Sock.hpp"
#include "Err.hpp"
#include "Epoller.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

namespace tcpserver
{
    static const uint16_t defaultport = 8080;
    static const int num = 64;

    class TcpServer;

    class Connection;
    using func_t = std::function<void(Connection *)>; // 回调函数用于不同的sock事件处理
    // using handler_t = std::function<void(const std::string &package)>;

    // 由于 接收\发送 的数据，不能确定是否为一个完整的报文，也无法确定读完就是完整报文，所以需要缓冲区进行保存
    // 之所以要保存住是因为，我们发送数据之后缓冲区不能设置为直接覆盖式的，这样子每一轮不完整报文的可能性非常大

    class Connection
    {
    public:
        Connection(int sock, TcpServer *tsp)
            : sock_(sock), tsp_(tsp)
        {
        }
        void Register(func_t r, func_t s, func_t e) // 注册方法
        {
            recver_ = r;
            sender_ = s;
            excepter_ = e;
        }
        void Close()
        {
            close(sock_);
        }
        ~Connection()
        {
        }

    public:
        int sock_;
        std::string inbuffer_;  // 输入缓冲区
        std::string outbuffer_; // 输入缓冲区

        func_t recver_;   // 负责从sock中读
        func_t sender_;   // 负责向sock中写
        func_t excepter_; // 处理sock IO时上面出现的异常事件

        TcpServer *tsp_; // 回值指针
    };

    class TcpServer
    {
    private:
        void Recver(Connection *conn)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t s = recv(conn->sock_, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    conn->inbuffer_ += buffer; // 先将数据存储起来
                    logMessage(DEBUG, "\n%s", conn->inbuffer_);
                    service_(conn); //读到一部分数据就存储起来
                }
                else if (s == 0)
                {
                    // 对方把链接关闭了
                    if (conn->excepter_)
                    {
                        conn->excepter_(conn);
                        return;
                    }
                    else
                    {
                        if (errno == EAGAIN || errno == EWOULDBLOCK)
                            break; // 没有数据而已
                        else if (errno == EINTR)
                            continue; // 中断则继续
                        else
                        {
                            if (conn->excepter_)
                            {
                                conn->excepter_(conn);
                                return;
                            }
                        }
                    }
                }
            }
        }
        void Sender(Connection *conn)
        {
            while (true)
            {
                ssize_t s = send(conn->sock_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
                if (s > 0)
                {
                    if (conn->outbuffer_.empty())
                    {
                        // EnableReadWrite(conn, true, false);
                        break; // 数据发完了}
                    }
                    else
                        conn->outbuffer_.erase(0, s);
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break; // 数据满了，无法发送
                    if (errno == EINTR)
                        continue; // 中断发送再启动
                    else
                    {
                        if (conn->excepter_)
                        {
                            conn->excepter_(conn);
                            return;
                        }
                    }
                }
                if (!conn->outbuffer_.empty())
                    conn->tsp_->EnableReadWrite(conn, true, true);
                else
                    conn->tsp_->EnableReadWrite(conn, true, false);
            }
        }
        void Excepter(Connection *conn)
        {
            logMessage(DEBUG, "Excepter begin"); // 读写的所有问题都到这
            epoller_.Control(conn->sock_, 0, EPOLL_CTL_DEL);
            conn->Close(); // 先在epoll移除再关闭描述符
            connections_.erase(conn->sock_);

            logMessage(DEBUG, "关闭%d 文件描述符的所有的资源", conn->sock_);

            delete conn;
        }

        void Accepter(Connection *conn) // listensocket的获取链接
        {
            for (;;) // epoll的ET模式只在变化时发送，我们不能只接收一个sock就结束了
            {
                std::string clientip;
                uint16_t clientport;
                int err = 0;
                int sock = sock_.Accept(&clientip, &clientport, &err);
                if (sock > 0)
                {
                    AddConnection(sock, EPOLLIN | EPOLLET,
                                  std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                                  std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
                    logMessage(DEBUG, "get a new link, info: [%s:%d]", clientip.c_str(), clientport);
                }
                else
                {
                    if (err == EAGAIN || err == EWOULDBLOCK)
                        break;
                    else if (err == EINTR)
                        continue;
                    else
                        break;
                }
            }
        }

        void AddConnection(int sock, uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            // 1.首先将sock创建自己的connection，初始化后加入到unordered_map中
            if (events & EPOLLET)
                Util::SetNonBlock(sock);
            Connection *conn = new Connection(sock, this);

            // 2.给对应的socket设置对应的回调方法
            conn->Register(recver, sender, excepter);

            // 3.其次将sock与它关心的事件“写透式”注册到epoll中，让epoll帮我们关心
            bool ret = epoller_.AddEvent(sock, events);
            assert(ret);
            (void)ret;

            // 4.需要把这组链接添加到connections_对象中
            connections_.insert(std::make_pair(sock, conn));

            logMessage(DEBUG, "add new sock : %d in epoll and unordered_map", sock);
        }

        bool IsConnectionExists(int sock)
        {
            auto iter = connections_.find(sock);
            return iter != connections_.end();
        }

        void Loop(int timeout)
        {
            int n = epoller_.Wait(revs_, num_, timeout); // 得到就绪事件
            for (int i = 0; i < n; i++)
            {
                int sock = revs_[i].data.fd;
                uint32_t events = revs_[i].events;

                // 异常事件发生，我们同时设置读写事件就绪
                // 目的:将所有的异常问题转换为读写问题
                if (events & EPOLLERR)
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT);

                // 二合一
                // if ((events & EPOLLIN) && sock == sock_.Fd()) // 监听事件就绪 -- 获取新链接
                // {
                //     connections_[sock]->recver_(connections_[sock]); // 调用Accepter方法
                // }
                // if((events & EPOLLIN)&&sock != sock_.Fd()) //普通文件描述符
                // {
                //     connections_[sock]->recver_(connections_[sock]); //普通文件调用函数
                // }
                if ((events & EPOLLIN) && IsConnectionExists(sock) && (connections_[sock]->recver_)) // 监听事件就绪 -- 获取新链接
                {
                    connections_[sock]->recver_(connections_[sock]); // 监听事件就绪 或者 普通文件描述符
                }

                if (events & EPOLLOUT && IsConnectionExists(sock) && (connections_[sock]->sender_))
                {
                    connections_[sock]->sender_(connections_[sock]);
                }
            }
        }

    public:
        TcpServer(func_t func, uint16_t port = defaultport)
            : port_(port), revs_(nullptr), service_(func)
        {
        }

        void InitServer()
        {
            // 1. 创建socket
            sock_.Socket();
            sock_.Bind(port_);
            sock_.Listen();
            // 2.构建epoll
            epoller_.Create();
            // 3.将目前唯一的套接字先添加到epoll中,想要添加到epoll先将fd设置为非阻塞
            // listensock也是一个socket,也要看成一个Connection
            AddConnection(sock_.Fd(), EPOLLIN | EPOLLET, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
            revs_ = new struct epoll_event[num];
            num_ = num;
        }

        void EnableReadWrite(Connection *conn, bool readable, bool writeable) // 对特定套接字打开指定的读事件和写事件
        {
            uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
            epoller_.Control(conn->sock_, event, EPOLL_CTL_MOD); // 该模式
        }

        void Dispatcher() // 事件派发器
        {
            while (true)
            {
                int timeout = 1000;
                Loop(timeout); // 循环一次的执行
                logMessage(DEBUG, "timeout...");
            }
        }

        ~TcpServer()
        {
            sock_.Close();
            epoller_.Close();
            if (revs_ == nullptr)
                delete[] revs_;
        }

    private:
        uint16_t port_;
        Sock sock_;
        Epoller epoller_;
        std::unordered_map<int, Connection *> connections_; // socket链接和Connection对应
        struct epoll_event *revs_;
        int num_;
        // handler_t handler_;
        func_t service_;
    };
}